package com.pnnd.stream;

import com.pnnd.utils.CollectionUtils;
import com.sun.xml.internal.rngom.parse.compact.EOFException;

import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidParameterException;
import java.util.List;

/**
 * 对于不连续的segment，进行内存变换
 */
public class MultiplySegmentInputStream extends InputStream implements Seekable,Freshable {

    private List<MemorySegment> segments;

    private int mark;
    private int offset;
    private int limit;

    public MultiplySegmentInputStream(List<MemorySegment> segments){
        this.segments = segments;
    }

    /**
     * 获取所有segment内存的总和，跟limit未知无关
     * @return int
     */
    public int capacity(){

        int result = 0;

        for(MemorySegment segment:this.segments){

            if(result >= segment.size())
                result += segment.size();
        }

        return result;
    }

    private int getIndex(){
        int result = this.offset;

        for(MemorySegment segment:this.segments){

            if(result >= segment.size())
                result -= segment.size();
        }

        return result;
    }

    private MemorySegment getSegment() throws EOFException {
        int result = this.offset;

        for(MemorySegment segment:this.segments){

            if(result > segment.size())
                result -= segment.size();
            else
                return segment;
        }

        throw new EOFException();
    }

    private void checkReadLen(int len) throws EOFException {
        if(this.offset+len > this.limit){
            throw new EOFException();
        }
    }

    @Override
    public int read() throws IOException {

        try {
            this.checkReadLen(1);
        }catch (EOFException e){
            return -1;
        }

        int index = this.getIndex();

        MemorySegment segment = this.getSegment();

        byte value = segment.get(index);

        this.offset++;

        return value;
    }

    @Override
    public int read(byte b[]) throws IOException {

        return read(b, 0, b.length);
    }

    @Override
    public int read(byte b[], int off, int len) throws IOException {
        this.checkReadLen(len);

        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

        int c = read();
        if (c == -1) {
            return -1;
        }
        b[off] = (byte)c;

        int i = 1;
        try {
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        return i;
    }

    public List<MemorySegment> getSegments() {
        return CollectionUtils.readonly(this.segments);
    }

    public int getCurrentSegmentIndex() {
        return this.getIndex();
    }

    @Override
    public boolean markSupported() {
        return true;
    }

    @Override
    public synchronized void mark(int readlimit) {

        if(readlimit > this.offset || readlimit<this.limit){
            throw new IndexOutOfBoundsException();
        }

        this.mark = readlimit;
    }

    @Override
    public synchronized void reset() throws IOException {
        this.offset = mark;
    }

    @Override
    public void seek(int address) {

        if(address<0 || address>this.limit){
            throw new InvalidParameterException("address");
        }

        this.offset = address;
    }

    @Override
    public void fresh(int limit) {
        if(this.limit > limit){
            throw new InvalidParameterException("limit");
        }

        this.limit = limit;
    }
}
